package com.da.core;

import com.da.exception.PluginException;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;

/**
 * @author da
 * @time 2024/7/11 上午 11:53
 * * 插件管理
 * *  * 1.初始化插件目录
 * *  * 2.扫描插件目录
 * *  *  * 3.启动插件
 */
public class DPlugin {
    /**
     * * 插件目录
     */
    private final Path pluginDir;
    /**
     * 本次扫描出来的插件
     */
    private final List<Class<?>> scanPlugin = new ArrayList<>();

    public DPlugin(String path) {
        this.pluginDir = Paths.get(path);
        //初始化存放插件目录
        initPluginDir();
        //扫描插件
        this.scanPlugin.addAll(scanPluginDir());
    }

    /**
     * * 启动扫描到的插件
     */
    public void start() {
        start(this.scanPlugin);
    }

    private void start(List<Class<?>> classes) {
        //执行最新扫描出来的插件
        for (Class<?> plugin : classes) {
            try {
                Plugin o = (Plugin) plugin.getConstructor().newInstance();
                o.execute();
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                     NoSuchMethodException e) {
                throw new PluginException(e);
            }
        }
    }

    /**
     * * 扫描插件
     */
    public List<Class<?>> scanPluginDir() {
        List<Class<?>> classes = new ArrayList<>();
        try (Stream<Path> plugins = Files.list(this.pluginDir)) {
            plugins.forEach(p -> handlePlugin(p, classes));
        } catch (IOException e) {
            throw new PluginException(e);
        }
        return classes;
    }

    /**
     * 获取到所有的插件实例
     *
     * @param path    jar包路径
     * @param classes 实现类列表
     */
    private void handlePlugin(Path path, List<Class<?>> classes) {
        if (path.toString().endsWith(".jar")) {
            File jar = path.toFile();
            CustomClassLoader classLoader = new CustomClassLoader(jar.getPath());
            try (JarFile jarFile = new JarFile(jar)) {
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    if (!entry.isDirectory()) {
                        String className = entry.getName().replace("/", ".");
                        if (className.endsWith(".class")) {
                            className = className.substring(0, className.length() - 6);
                            Class<?> aClass = classLoader.loadClass(className);
                            //判断是否是插件的实现类
                            if (!aClass.isInterface() && Plugin.class.isAssignableFrom(aClass)) {
                                classes.add(aClass);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                throw new PluginException(e);
            } catch (ClassNotFoundException e) {
                throw new PluginException(e);
            }
        }
    }

    /**
     * * 初始化插件目录
     */
    private void initPluginDir() {
        if (!Files.exists(this.pluginDir)) {
            try {
                Files.createDirectory(this.pluginDir);
            } catch (IOException e) {
                throw new PluginException(e);
            }
        }
    }

    /**
     * * 获取插件列表
     *
     * @return 插件列表
     */
    public List<Class<?>> getPlugins() {
        return this.scanPlugin;
    }

    /**
     * * 监听插件目录
     */
    public void watch() {
        new Thread(() -> {
            while (true) {
                start(scanPluginDir());
                //休眠2秒再重新扫描
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new PluginException(e);
                }
            }
        }).start();
    }
}
